// VBConversions Note: VB project level imports
using System.Data;
using DevExpress.XtraEditors.Controls;
using System.Diagnostics;
using System.Xml.Linq;
using DevExpress.XtraEditors.Repository;
using System.Collections.Generic;
using System.Data.SqlClient;
using Microsoft.VisualBasic;
using System.Configuration;
using System.Collections;
using DevExpress.XtraEditors;
using System.Data.Sql;
using Microsoft.SqlServer;
using System.Windows.Forms;
using System;
using DevExpress.XtraReports;
using Microsoft.SqlServer.Management.Smo;
using DevExpress;
using System.Linq;
using System.IO;
// End of VB project level imports

using System.Runtime.Serialization;

namespace SCIEF.SysBFW.CommonExt
{
	[Serializable()]public class MetaDataTable : ExtendedDataTable
	{
		//Implements IDisposable
		
#region  Constructor
		public MetaDataTable()
		{
		}
		/// <summary>
		/// Creates a new MetaDataTable by initializing it's adapter.
		/// Defines what data will be load into the DataTable and how it will
		/// be managed.
		/// </summary>
		/// <param name="adapter"></param>
		/// <remarks>The SqlDataAdapter should have a valid SelectCommand and include a rowversion  (alias timestamp) field</remarks>
		public MetaDataTable(SqlDataAdapter adapter) : base(adapter)
		{
		}
		/// <summary>
		/// Creates a new MetaDataTable by initializing it's adapter.
		/// Defines what data will be load into the DataTable and how it will
		/// be managed.
		/// </summary>
		/// <param name="adapter"></param>
		/// <param name="command">MetaCommand (select)</param>
		/// <remarks>The "select" statement in the command parameter must include a timestamp (alias rowversion) field</remarks>
		public MetaDataTable(SqlDataAdapter adapter, string command) : base(adapter, command)
		{
		}
		public MetaDataTable(string tableName) : base(tableName)
		{
		}
		public MetaDataTable(string tableName, string tableNamespace) : base(tableName, tableNamespace)
		{
		}
		protected MetaDataTable(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context)
		{
		}
#endregion
		
#region  Field
		//Private sqlAdapter As New SqlDataAdapter()
		public SqlCommand Command; // VBConversions Note: Initial value of "Base.Connection.Command()" cannot be assigned here since it is non-static.  Assignment has been moved to the class constructors.
#endregion
#region  Property Field
		private string _queryName;
		private MetaQuery _metaCommand = new MetaQuery();
		private DataTable _metaData;
		private DataTable _metaParams;
#endregion
#region  Event
		public delegate void OnRowUpdatingEventHandler(object sender, SqlRowUpdatingEventArgs e);
		private OnRowUpdatingEventHandler OnRowUpdatingEvent;
		
		public event OnRowUpdatingEventHandler OnRowUpdating
		{
			add
			{
				OnRowUpdatingEvent = (OnRowUpdatingEventHandler) System.Delegate.Combine(OnRowUpdatingEvent, value);
			}
			remove
			{
				OnRowUpdatingEvent = (OnRowUpdatingEventHandler) System.Delegate.Remove(OnRowUpdatingEvent, value);
			}
		}
		
		public delegate void OnRowUpdatedEventHandler(object sender, SqlRowUpdatedEventArgs e);
		private OnRowUpdatedEventHandler OnRowUpdatedEvent;
		
		public event OnRowUpdatedEventHandler OnRowUpdated
		{
			add
			{
				OnRowUpdatedEvent = (OnRowUpdatedEventHandler) System.Delegate.Combine(OnRowUpdatedEvent, value);
			}
			remove
			{
				OnRowUpdatedEvent = (OnRowUpdatedEventHandler) System.Delegate.Remove(OnRowUpdatedEvent, value);
			}
		}
		
#endregion
#region  Property
		/// <summary>
		/// Shortcut to test if we have Lookup to proceed by the consumer (Ex.: ExtendedGrid)
		/// </summary>
		/// <value></value>
		/// <returns></returns>
		/// <remarks></remarks>
		public bool HasLookup
		{
			get
			{
				return this.MetaCommand.HasLookup;
			}
		}
		/// <summary>
		/// Shortcut to test if we have a guid Field
		/// </summary>
		/// <value></value>
		/// <returns></returns>
		/// <remarks></remarks>
		public string GUIDField
		{
			get
			{
				return this.MetaCommand.GUIDField;
			}
		}
		/// <summary>
		/// Shortcut to test if we have LOV to proceed by the consumer (Ex.: ExtendedGrid)
		/// </summary>
		/// <value></value>
		/// <returns></returns>
		/// <remarks></remarks>
		public bool HasLov
		{
			get
			{
				return this.MetaCommand.HasLov;
			}
		}
		/// <summary>
		/// Lookup list that will be process by the main consumer
		/// </summary>
		/// <value></value>
		/// <returns></returns>
		/// <remarks></remarks>
		public Dictionary<string, Guid> ListOfLookup
		{
			get
			{
				return this.MetaCommand.ListOfLookup;
			}
		}
		/// <summary>
		/// Lookup list that will be process by the main consumer
		/// </summary>
		/// <value></value>
		/// <returns></returns>
		/// <remarks></remarks>
		public Dictionary<string, Guid> ListOfVisibleLookup
		{
			get
			{
				return this.MetaCommand.ListOfVisibleLookup;
			}
		}
		
		/// <summary>
		/// LOV list that will be process by the main consumer
		/// </summary>
		/// <value></value>
		/// <returns></returns>
		/// <remarks></remarks>
		public Dictionary<string, Guid> ListOfLov
		{
			get
			{
				return this.MetaCommand.ListOfLov;
			}
		}
		/// <summary>
		/// LOV list that will be process by the main consumer
		/// </summary>
		/// <value></value>
		/// <returns></returns>
		/// <remarks></remarks>
		public Dictionary<string, Guid> ListOfVisibleLov
		{
			get
			{
				return this.MetaCommand.ListOfVisibleLov;
			}
		}
		/// <summary>
		/// All the details of a Metacommand. SQL etc...
		/// </summary>
		/// <value></value>
		/// <returns></returns>
		/// <remarks></remarks>
		public MetaQuery MetaCommand
		{
			get
			{
				return _metaCommand;
			}
			set
			{
				if (value != null)
				{
					_metaCommand = value;
					//If we assign the object _metaCommand from the consumer we must assign the _queryname
					_queryName = _metaCommand.QueryName;
				}
			}
		}
		/// <summary>
		/// This Datatable contains the metacolumn properties to be applied by the consumer (Ex.: ExtendedGrid)
		/// </summary>
		/// <value></value>
		/// <returns></returns>
		/// <remarks></remarks>
		public DataTable MetaData
		{
			get
			{
				if (_metaData == null)
				{
					this.MetaCommand.FillMetaColumns();
					_metaData = this.MetaCommand.MetaColumns;
				}
				return _metaData;
			}
			set
			{
				_metaData = value;
			}
		}
		/// <summary>
		/// This Datatable contains the Param properties
		/// </summary>
		/// <value></value>
		/// <returns></returns>
		/// <remarks></remarks>
		public DataTable MetaParams
		{
			get
			{
				if (_metaParams == null)
				{
					this.MetaCommand.FillMetaParams();
					_metaParams = this.MetaCommand.MetaParams;
				}
				return _metaParams;
			}
			set
			{
				_metaParams = value;
			}
		}
		/// <summary>
		/// Contain the QueryName of the MetaCommand/Metaqueries
		/// TestPoint: Define your queryName here to get the data
		/// </summary>
		/// <value></value>
		/// <returns></returns>
		/// <remarks></remarks>
		public string QueryName
		{
			get
			{
				return _queryName;
			}
			set
			{
				_queryName = value;
				this.TableName = _queryName;
			}
		}
#endregion
#region  Method
#region Sub
		/// <summary>
		/// This function has the purpose of returning a datarow from the dataTable loaded in memory using the GUID as key filter.
		/// </summary>
		/// <param name="id">System.Guid</param>
		/// <remarks>The architectural purpose of this function is to permit a call from a consumer to recuperate a datarow object.
		/// That datarow is used mostly in systembox to serve has the datasource for a BLL entity for binding to control that are not directly bindeable to ADO.datatable.
		/// The usage of the Microsoft Propertygrid is an good example and primary usage of GetFromtable
		///  </remarks>
		
		public DataRow GetFromTable(Guid id)
		{
			DataRow _dataRow = Rows.Find(id);
			//Exception are handled by Exceptionhandling Class
			return _dataRow;
		}
		public void GetFromDB(Guid id)
		{
			throw (new NotImplementedException("GetFromDB"));
		}
		public void GetAll()
		{
			throw (new NotImplementedException("GetAll"));
		}
		/// <summary>
		/// Prepare the CUD comand for the adapter then update and Accepts Changes.
		/// By design we go get the queries for CUD and Add the handler on the adapter, so we go get de RecordVersion
		/// before it was done in the FillBusinessApplicationData
		/// You will neet to have a OnRowUpdating in your PL if you need to call SP for Sequence
		/// </summary>
		/// <param name="continueUpdateOnError">True - Will process all row event if an error. False - Stop the update on the first row on error </param>
        /// <param name="dtTable"></param>
        /// <param name="reload"></param>
        /// /// <returns></returns>
		/// <remarks>
		/// It is very important to verify that the newrecord function is called to initialise the key field of the record.
		/// It would be possible in extreme case to overrides that function by initializing the key field of a record in the overriden menber of a factory class in the event initnewmasterrow or initnewMasterRow</remarks>
		public int ApplyChanges(bool continueUpdateOnError = true, DataTable dtTable = null, bool reload = true)
		{
			int nbrec = 0;
			//Testpoint : Applychanges and METACOMMANDCLAS_CUD
			if (this.MetaCommand.CommandClassID == Base.IdRepository.METALIST_METACOMMANDCLAS_CUD)
			{
				//No need to go get the MetaQuery from the DB. Query for CUD is the same.
				this.BuildCUDCommands(this.MetaCommand);
			}
			else
			{
				AssignAdapterCommandForCUD();
			}
			//Ajouter les AddHandler seulement si on modifie on peut faire du CUD.
			if (this.Adapter.UpdateCommand != null || this.Adapter.DeleteCommand != null || this.Adapter.InsertCommand != null)
			{
				this.Adapter.RowUpdated += new System.Data.SqlClient.SqlRowUpdatedEventHandler(RowUpdated);
				this.Adapter.RowUpdating += new System.Data.SqlClient.SqlRowUpdatingEventHandler(RowUpdating);
			}
			//
			this.Adapter.ContinueUpdateOnError = continueUpdateOnError;
			if (dtTable != null)
			{
				nbrec = this.Adapter.Update(dtTable);
			}
			else
			{
				nbrec = this.Adapter.Update(this);
			}
			if (nbrec > 0)
			{
				this.AcceptChanges();
				//OutStandingIssue: Recharge la table... aleblanc - 2011-05-02
				//Remplace le RefechRowVersion... a suivre
				//A cause des concurrency sur les childs...
				//ca ralentit le systeme car on reli toute les lignes....
				if (reload == true)
				{
					this.Adapter.Fill(this);
				}
			}
			//
			//Ajouter les AddHandler seulement si on modifie on peut faire du CUD.
			if (this.Adapter.UpdateCommand != null || this.Adapter.DeleteCommand != null || this.Adapter.InsertCommand != null)
			{
				this.Adapter.RowUpdated -= new System.Data.SqlClient.SqlRowUpdatedEventHandler(RowUpdated);
				this.Adapter.RowUpdating -= new System.Data.SqlClient.SqlRowUpdatingEventHandler(RowUpdating);
			}
			
			return nbrec;
		}
		/// <summary>
		/// Fill local field class MetaCommand
		/// </summary>
		/// <remarks>Propertie QueryName must be set</remarks>
		public void GetMetaCommand()
		{
			this._metaCommand = MetaQuery.GetMetaQueryByName(this.QueryName);
		}
		
		public void GetMetaCommand(string metaQueryName)
		{
			this._metaCommand = MetaQuery.GetMetaQueryByName(metaQueryName);
			
		}
		/// <summary>
		/// Main method of the class. Load all then info. needed.
		/// </summary>
		/// <param name="queryName"></param>
		/// <remarks></remarks>
		public void FillBusinessApplicationData(string queryName)
		{
			this.QueryName = queryName;
			this.FillBusinessApplicationData();
		}
		
		public void FillResultQuery(SqlCommand SqlCmd)
		{
			
			Command.CommandText = this.MetaCommand.SQLStatement;
			this.Adapter.SelectCommand = SqlCmd;
			this.Adapter.Fill(this);
			//Construit nos listes de LOV / TR pour que nos grilles fonctionne comme il le faut. - AL 2013-03-19
			this.MetaCommand.GetLOV_LKP();
		}
		/// <summary>
		/// Main method of the class. Load all the info. needed. When queryName is assign.
		///
		/// If you want to do CUD with one query:
		/// 1- Make sure your ClassCommand is of Type SELECT For CUD
		/// 2- You have a TStamp field or RecordVersion in your SELECT Statement
		/// 3- Crtd_DT, Crtd_UId, LUpd_UId, LUpd_DT are in your SELECT Statement
		/// </summary>
		/// <remarks></remarks>
		public void FillBusinessApplicationData()
		{
			//Here we go get the MetaCommand if the consumer did not set it.
			//The consumer might have an extra filter to apply to the query not define in the MetaCommand
			
			//HACK: String.IsNullOrEmpty(Me.MetaCommand.SQLSelect) - Aleblanc 2011-02-14
			//Me.MetaCommand est tjrs different de nothing car initialiser au début... Si le SQLSelect a rien ca veux dire que la MetaCommand est pas loader
			//
			if (string.IsNullOrEmpty(this.MetaCommand.QueryName))
			{
				this.GetMetaCommand();
			}
			if (this.MetaCommand != null)
			{
				try
				{
					Command.CommandText = this.MetaCommand.SQLStatement;
					this.Adapter.SelectCommand = Command;
					this.Adapter.Fill(this);
					//"CUD Ready": SELECT have a record version or TsTamp and does not have JOIN
					//If SelectCommand is "CUD Ready" then
					//Me._businessApplicationData will be able to support CRUD
					//
					//TestPoint: Ici on a le nom de la metaquery
					this.TableName = this.MetaCommand.QueryName;
					if (!string.IsNullOrEmpty(this.MetaCommand.GUIDField))
					{
						if (this.Columns.Contains(this.MetaCommand.GUIDField))
						{
							//
							//OutStandingIssues: Est-ce que le PrimaryKey doit être définie si la Meta est de type Read? - aleblanc 2011-04-05
							//                   Remarque: Si on défini une PrimaryKey elle doit être unique.
							this.PrimaryKey = new DataColumn[] {this.Columns[this.MetaCommand.GUIDField]};
							this.Columns[this.MetaCommand.GUIDField].AllowDBNull = true;
						}
						else
						{
							throw (new Exception(string.Format("GUIDFIeld {0}, Does Not Exist In Your MetaQuery Select Statement", this.MetaCommand.GUIDField)));
						}
					}
					//Serait bien de pouvoir aller chercher les default values de la BD si on les définie pas et que les Champs sont Not Null Allowed - Aleblanc
					foreach (KeyValuePair<string, object> defaultValue in this.MetaCommand.DefaultParentValue)
					{
						if (this.Columns.Contains((string) defaultValue.Key))
						{
							this.Columns[System.Convert.ToInt32(defaultValue.Key)].DefaultValue = defaultValue.Value;
						}
						else
						{
							throw (new Exception(string.Format("Column {0}, Does Not Exist In Your MetaQuery Select Statement", defaultValue.Key)));
						}
					}
					//
					if (this.Columns.Contains("RecordVersion"))
					{
						this.Columns["RecordVersion"].AllowDBNull = true;
						this.Columns["RecordVersion"].Unique = false;
					}
					//
					if (this.Columns.Contains("TStamp"))
					{
						this.Columns["TStamp"].AllowDBNull = true;
						this.Columns["TStamp"].Unique = false;
					}
					//
					//HACK:
					//This is all the system Fields that we need to update to keep our audit
					//Even if we have trigger it seem they do not update our field
					//
					if (this.Columns.Contains("LUpd_UId"))
					{
						this.Columns["LUpd_UId"].DefaultValue = SysBFW.CommonExt.Base.AppManager.UserName;
					}
					
					if (this.Columns.Contains("Crtd_UId"))
					{
						this.Columns["Crtd_UId"].DefaultValue = SysBFW.CommonExt.Base.AppManager.UserName;
					}
					
					if (this.Columns.Contains("LUpd_DT"))
					{
						this.Columns["LUpd_DT"].DefaultValue = DateTime.Now;
					}
					
					if (this.Columns.Contains("Crtd_DT"))
					{
						this.Columns["Crtd_DT"].DefaultValue = DateTime.Now;
					}
					//aleblanc - 2011-04-11 Déplaceer dans ApplyChanges
					//If Me.MetaCommand.CommandClassID = Base.IdRepository.METALIST_METACOMMANDCLAS_CUD Then
					//    'No need to go get the MetaQuery from the DB. Query for CUD is the same.
					//    Me.BuildCUDCommands(Me.MetaCommand)
					//Else
					//    AssignAdapterCommandForCUD()
					//End If
					//'Ajouter les AddHandler seulement si on modifie on peut faire du CUD.
					//If Me.Adapter.UpdateCommand IsNot Nothing _
					//    OrElse Me.Adapter.DeleteCommand IsNot Nothing _
					//    OrElse Me.Adapter.InsertCommand IsNot Nothing Then
					//    AddHandler Me.Adapter.RowUpdated, AddressOf RowUpdated
					//    AddHandler Me.Adapter.RowUpdating, AddressOf RowUpdating
					//End If
					this.MetaCommand.GetLOV_LKP();
				}
				catch (Exception ex)
				{
					throw (new Exception(string.Format("Query error MetaDataTable.FillBusinessApplicationData(){0}{1}{0}MetaQuery: {2}", "\r\n", ex.Message, this.QueryName)));
				}
			}
			else
			{
				throw (new Exception(string.Format("Command is not Define: MetaDataTable.FillBusinessApplicationData(){0}MetaQuery: {1}", "\r\n", this.QueryName)));
			}
		}
		/// <summary>
		/// Here we go get the child query to do Create, Update and Delete (CUD)
		/// </summary>
		/// <remarks>By design, Scief model for Store Proc. We use the same for Insert and Update</remarks>
		private void AssignAdapterCommandForCUD()
		{
			foreach (MetaQuery mq in this.MetaCommand.ChildQueries)
			{
				if (mq.CommandClassID == Base.IdRepository.METALIST_METACOMMANDCLAS_DELETE)
				{
                    this.Adapter.DeleteCommand = MetaQuery.GetSPCmdByMeta(mq);
				}
				else if (mq.CommandClassID == Base.IdRepository.METALIST_METACOMMANDCLAS_UPDATE)
				{
                    this.Adapter.UpdateCommand = MetaQuery.GetSPCmdByMeta(mq);
				}
				else if (mq.CommandClassID == Base.IdRepository.METALIST_METACOMMANDCLAS_CREATE)
				{
                    this.Adapter.InsertCommand = MetaQuery.GetSPCmdByMeta(mq);
				}
				else if (mq.CommandClassID == Base.IdRepository.METALIST_METACOMMANDCLAS_CUD)
				{
					this.BuildCUDCommands(mq);
				}
			}
			//By design On assume que le insert et le update sont pareil si les SP sont pas la.
			//de plus ca nous sauve un acces BD si pareil...
			
			//Dans les Metaquery, la classe Create assigne le InsertCommand et la Update le UpdateCommand
			//Si un des 2 est absent on l'assigne de l'autre.
			if (this.Adapter.UpdateCommand == null && this.Adapter.InsertCommand != null)
			{
				//Ici Clone est utilisé a cause que je veux une nouvelle objet de la command. Je ne veux pas la référence
				//Les paramètres sont partagé si on ne fait pas de clone
				this.Adapter.UpdateCommand = this.Adapter.InsertCommand.Clone();
			}
			if (this.Adapter.InsertCommand == null && this.Adapter.UpdateCommand != null)
			{
				//Ici Clone est utilisé a cause que je veux une nouvelle objet de la command. Je ne veux pas la référence
				//Les paramètres sont partagé si on ne fait pas de clone
				this.Adapter.InsertCommand = this.Adapter.UpdateCommand.Clone();
			}
		}
		/// <summary>
		/// Use by the Adapter during an Insert/Update/Delete
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		/// <remarks>Use to modified the row or parameter during the execution</remarks>
		private void RowUpdating(object sender, SqlRowUpdatingEventArgs e)
		{
			if (OnRowUpdatingEvent != null)
				OnRowUpdatingEvent(sender, e);
		}
		/// <summary>
		/// Use by the Adapter after an Insert/Update/Delete
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		/// <remarks></remarks>
		private void RowUpdated(object sender, SqlRowUpdatedEventArgs e)
		{
			// Conditionally execute this code block on inserts and update.
			//If Not e.Status = UpdateStatus.ErrorsOccurred Then
			//    If e.StatementType = StatementType.Update Or e.StatementType = StatementType.Insert Then
			//        Dim recordVersion As Byte() = Me.MetaCommand.RefetchRowVersion(CType(e.Row(Me.MetaCommand.GUIDField), Guid))
			//        If recordVersion IsNot Nothing Then
			//            If e.Status = UpdateStatus.Continue Then
			//                e.Row.Table.Columns(Me.MetaCommand.RecordVersionField).ReadOnly = False
			//                e.Row(Me.MetaCommand.RecordVersionField) = recordVersion
			//            End If
			//        End If
			//    End If
			//End If
			if (OnRowUpdatedEvent != null)
				OnRowUpdatedEvent(sender, e);
		}
#endregion
#endregion
#region  IDisposable Support
		// IDisposable
		private bool disposedValue = false; // To detect redundant calls
		protected new void Dispose(bool disposing)
		{
			base.Dispose(disposing);
			if (!this.disposedValue)
			{
				if (disposing)
				{
					// TODO: free other state (managed objects).
					//RemoveHandler Me.Adapter.RowUpdated, AddressOf RowUpdated
					//RemoveHandler Me.Adapter.RowUpdating, AddressOf RowUpdating
					this.Command.Dispose();
					if (_metaData != null)
					{
						this._metaData.Dispose();
					}
					
					this._metaCommand.Dispose();
					//
					this.Command = null;
					this._metaData = null;
					this._metaCommand = null;
				}
				// TODO: free your own state (unmanaged objects).
				// TODO: set large fields to null.
			}
			this.disposedValue = true;
		}
		
		// This code added by Visual Basic to correctly implement the disposable pattern.
		public new void Dispose()
		{
			// Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
			Dispose(true);
			GC.SuppressFinalize(this);
		}
#endregion
		
	}
	
}
